package com.wei.czz.framework.blog.manager;

import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.blog.tag.TagDto;
import com.wei.czz.common.dto.blog.tag.TagEnumsDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.RandomNumUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.blog.tag.TagVo;
import com.wei.czz.common.vo.common.UpdateStatusVo;
import com.wei.czz.common.vo.blog.tag.TagFormVo;
import com.wei.czz.framework.blog.entity.TagEntity;
import com.wei.czz.framework.blog.service.TagService;
import com.wei.czz.framework.common.handler.ThrottleHandler;
import com.wei.czz.framework.common.handler.redis.RedisHandler;
import com.wei.czz.framework.common.handler.redis.RedisStringHandler;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2025-01-23 17:53:06
 * className: TagManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class TagManager {

    private static final Logger log = LoggerFactory.getLogger(TagManager.class);

    private final TagService tagService;

    private final RedisHandler redisHandler;

    private final RedisStringHandler redisStringHandler;

    private final ThrottleHandler throttleHandler;

    private final RedissonClient redissonClient;

    public void save(TagVo tagVo) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            判断博客标签是否存在
         */
        TagEntity tag = tagService.find(tagVo.getName());
        if (Objects.nonNull(tag)) {
            throw new CzzException(ResultEnum.DATA_DUPLICATION.getCode(), "标签已存在，请确认后再添加");
        }

        /*
            保存博客标签
         */
        TagEntity saveTag = new TagEntity();
        saveTag.setName(tagVo.getName())
                .setSort(tagVo.getSort())
                .setStatus(CommonEnum.ZERO.getValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userPo.getUsername())
                .setCreateUserId(userPo.getUserId());
        tagService.saveOrEdit(saveTag);

        // 删除缓存
        this.deleteTagCache();
    }

    public PageDto<TagDto> getPageList(TagFormVo tagFormVo) {

        String systemTagListKey = tagFormVo.getSystemTagListKey();
        // 查询Redis缓存，获取标签列表缓存
        PageDto<TagDto> pageDto = redisStringHandler.get(systemTagListKey);
        if (pageDto != null) {
            log.info("从缓存中获取到博客标签分页数据");
            throttleHandler.throttleRun(() -> {
                // 尝试更新缓存键的过期时间
                redisHandler.tryUpdateKeyExpire(systemTagListKey, 150, RandomNumUtils.random5Minutes(10));
            }, systemTagListKey, 1500, TimeUnit.MILLISECONDS);
            return pageDto;
        }
        // 获取Redis分布式锁对象
        RLock lock = redissonClient.getLock(RedisConstant.LOCK + systemTagListKey);
        // 加锁
        lock.lock();

        try {
            // double check
            pageDto = redisStringHandler.get(systemTagListKey);
            if (Objects.nonNull(pageDto)) {
                log.info("double check 从缓存中获取到博客标签分页数据");
                return pageDto;
            }

            /*
                分页获取博客标签
             */
            PageDto<TagEntity> _pageDto = tagService.getPageList(tagFormVo);
            if (_pageDto.isEmpty()) {
                pageDto = _pageDto.rebuild();
                // 缓存
                redisStringHandler.set(systemTagListKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);
                return pageDto;
            }

            List<TagEntity> tagList = _pageDto.getList();
            // 列表映射
            Function<TagEntity, TagDto> function = (tag) -> {
                TagDto tagDto = CopyUtils.map(tag, TagDto.class);
                tagDto.setStatusName(CommonEnum.getStatusName(tag.getStatus()));
                return tagDto;
            };
            List<TagDto> list = CopyUtils.mapList(tagList, function);
            // 封装
            pageDto = _pageDto.rebuild(list);

            // 缓存
            redisStringHandler.set(systemTagListKey, pageDto, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return pageDto;
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public TagDto getTag(Long id) {

        /*
            获取博客标签
         */
        TagEntity tag = tagService.get(id);

        return CopyUtils.map(tag, TagDto.class);
    }

    public TagEnumsDto getPageEnum() {
        TagEnumsDto tagEnumsDto = new TagEnumsDto();
        tagEnumsDto.setStatusEnumList(CommonEnum.getStatusList());
        tagEnumsDto.setUseStatus(CommonEnum.ZERO.getValue());
        tagEnumsDto.setDisableStatus(CommonEnum.ONE.getValue());
        return tagEnumsDto;
    }

    public void updateTag(TagVo tagVo) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            判断博客标签是否存在
         */
        TagEntity tag = tagService.find(tagVo.getName());
        if (Objects.nonNull(tag)) {
            throw new CzzException(ResultEnum.DATA_DUPLICATION.getCode(), "标签已存在，请确认后再添加");
        }

        /*
            更新博客标签
         */
        TagEntity updateTag = new TagEntity();
        updateTag.setId(tagVo.getId())
                .setName(tagVo.getName())
                .setSort(tagVo.getSort())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());
        tagService.saveOrEdit(updateTag);

        // 删除缓存
        this.deleteTagCache();
    }

    @Transactional
    public void updateTagStatus(UpdateStatusVo updateStatusVo) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        List<Long> idList = updateStatusVo.getIdList();
        Integer status = updateStatusVo.getStatus();

        // 列表映射
        List<TagEntity> updateTagList = CopyUtils.mapList(idList, id -> {
            TagEntity updateTag = new TagEntity();
            updateTag.setId(id)
                    .setStatus(status)
                    .setUpdateTime(date)
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getUserId());
            return updateTag;
        });
        /*
            更新博客标签
         */
        tagService.batchUpdate(updateTagList);

        // 删除缓存
        this.deleteTagCache();
    }

    @Transactional
    public void deleteTag(List<Long> idList) {
        // 获取请求用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();


        // 列表映射
        List<TagEntity> updateTagList = CopyUtils.mapList(idList, id -> {
            TagEntity updateTag = new TagEntity();
            updateTag.setId(id)
                    .setDeleteStatus(CommonEnum.ONE.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getUserId());
            return updateTag;
        });

        /*
            更新博客标签
         */
        tagService.batchUpdate(updateTagList);

        // 删除缓存
        this.deleteTagCache();
    }

    /**
     * 删除博客标签或者用户申请标签临时缓存数据
     */
    private void deleteTagCache() {
        redisHandler.fuzzyDelete(RedisConstant.STRING + RedisConstant.SYSTEM_TAG_LIST + RedisConstant.ASTERISK);
    }

}
